Reuse-first, not rewrite-first
Vendor 应接受的核心设计决策:在现有认证边界外增加适配层,不重写本地身份、权限或会话模型。
现有 Session Gateway、User Directory、Permission Engine、Settings UI shell、Feature Flags、Audit pipeline 原样复用。
只新增 SAML Adapter、Identity Link Service、SSO Audit Events;避免通用身份平台或协议框架。
SSO 通过租户开关启用;本地登录保留;身份映射版本化,可在不改用户主表的情况下回退。
Build / change / reuse map
Tags 支持语义检索;“Reuse Evidence”是 Vendor 压缩估算时必须采用的现有能力。
| Component | Responsibility | Action | Reuse evidence / boundary | Tags |
|---|---|---|---|---|
SamlProtocolAdapter | 解析 metadata、生成 AuthnRequest、验证 assertion 与签名 | NEW | 仅协议转换;不得创建业务会话或分配角色 | #saml#security |
IdentityLinkService | 将 IdP subject 映射到现有 local user;处理冲突和回滚 | NEW | 复用 User Directory;不复制用户资料 | #identity#migration |
SsoAuditEmitter | 标准化 SSO 登录、配置、绑定事件 | NEW | 复用现有 audit pipeline、retention 和 viewer | #audit |
SessionGateway | 接受 normalized principal,创建现有 session | SMALL CHANGE | 保持 cookie、TTL、logout 语义不变 | #session |
AdminSsoSettings | IdP 配置、证书上传、连接测试、启用开关 | SMALL CHANGE | 复用 Settings page shell、form、permissions | #admin-ui |
PermissionEngine | 继续作为唯一授权来源 | REUSE AS-IS | 忽略 IdP role claim;本期不做角色同步 | #authorization |
FeatureFlags | 租户级灰度、紧急关闭 | REUSE AS-IS | 不新增 rollout service | #rollout |
One normalized principal
新代码只负责将 SAML assertion 转为现有 Session Gateway 可接受的标准身份对象。
Choose Enterprise SSO
Validate assertion
Resolve local user
Create existing session
// Existing callback route — protocol and mapping stay behind interfaces. const assertion = await samlAdapter.validateResponse(request.body.SAMLResponse); const principal = await identityLinks.resolve({ tenantId: request.tenant.id, issuer: assertion.issuer, subject: assertion.nameId, email: assertion.attributes.email }); if (principal.kind === "conflict") { audit.emit("sso.identity.conflict", principal.evidence); return response.redirect("/login?sso_error=identity_conflict"); } // REUSE: existing session creation, cookie policy and permissions. return sessionGateway.start(principal.localUserId, response);
Minimal production delta
绿色节点是新增模块;白色节点为复用或小范围修改。点击切换详细/紧凑视图。
SP-initiated login
唯一同步关键路径。连接测试和迁移任务不得复用此请求路径执行批量操作。
Stable seams for parallel delivery
Vendor 可并行实现 UI、协议和映射,只要以下契约冻结。不得让 UI 直接依赖 SAML library 类型。
NEWNEWCHANGENEW| Endpoint | Purpose | Auth | Success | Notes |
|---|---|---|---|---|
GET /auth/sso/start | Create signed AuthnRequest and redirect | Public + tenant context | 302 IdP URL | RelayState is opaque, signed, 5-min TTL |
POST /auth/sso/callback | Validate response, link identity, start session | SAML response | 302 app home | Reject replay before mapping lookup |
GET /api/admin/sso/config | Read masked tenant config | Admin permission | 200 SsoConfigView | Never return private key |
PUT /api/admin/sso/config | Validate and save config | Admin permission + CSRF | 200 config version | Optimistic version required |
POST /api/admin/sso/test | Run non-persistent connection check | Admin permission | 200 TestResult | Must not enable SSO |
POST /api/admin/sso/enable | Enable tenant flag after readiness gates | Admin permission + CSRF | 204 | Reject unless last test passed |
| Code | HTTP / UX | Retry? | Required action |
|---|---|---|---|
SSO_ASSERTION_INVALID | 400 / generic login error | No | Audit reason internally; never expose assertion details |
SSO_ASSERTION_REPLAYED | 409 / restart login | New flow | Security alert threshold + correlation ID |
IDENTITY_CONFLICT | 409 / support reference | No | Add conflict queue record; do not auto-link |
SSO_CONFIG_STALE | 409 / reload settings | After reload | Optimistic concurrency protection |
IDP_UNAVAILABLE | 503 / local fallback if allowed | Yes | Do not create partial session |
One new table, no user rewrite
SSO 映射独立于 users,避免迁移修改主身份数据;配置进入现有加密 settings 存储。
CREATE TABLE identity_links ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, issuer VARCHAR(512) NOT NULL, subject VARCHAR(512) NOT NULL, local_user_id UUID NOT NULL REFERENCES users(id), state VARCHAR(16) NOT NULL DEFAULT 'active', version INT NOT NULL DEFAULT 1, linked_at TIMESTAMP NOT NULL, linked_by UUID, revoked_at TIMESTAMP, UNIQUE(tenant_id, issuer, subject) ); CREATE INDEX ix_identity_links_user ON identity_links(tenant_id, local_user_id);
- id PK
- status
- credentials
- tenant + issuer + subject UNIQUE
- local_user_id FK → users
- state + version for rollback
- no raw assertion
- sso.metadata_url
- sso.certificate_ref
- sso.enabled
- sso.config_version
Ordered for reuse and parallelism
每个包有固定输出与节省依据。Vendor 应按包报价,不应重新加入已复用基础设施的完整建设成本。
实现 VerifiedAssertion 类型、SamlProtocolAdapter contract、有效/无效 fixture。输出:contract tests。
metadata、AuthnRequest、签名、issuer/audience/time/replay 验证。输出:adapter + security tests。
migration、resolve、conflict、manual link、revoke、dry-run import。输出:repository + migration CLI。
NormalizedPrincipal → SessionGateway;映射 SSO events 到现有 audit envelope。
复用 Settings shell 和 form controls;新增 config、test、enable、login choice 和错误状态。
e2e、迁移 rehearsal、monitoring、tenant pilot、rollback evidence、runbook。
Evidence required for approval
“完成”必须由自动化结果和运行证据证明,而非仅演示 happy path。
| Requirement | Verification | Required evidence | Owner |
|---|---|---|---|
| Valid SAML login creates normal session | Integration test + browser e2e | Test ID SSO-E2E-001; cookie policy snapshot | Vendor |
| Invalid/replayed assertion rejected | Security contract suite | issuer, audience, expiry, signature, replay matrix | Vendor |
| Conflict never auto-links | Repository + e2e tests | Conflict queue record; zero user mutation | Vendor |
| Local login remains available | IdP outage scenario | Fallback e2e + feature flag screenshot | Vendor |
| Tenant isolation enforced | Cross-tenant negative tests | No link/config retrieval across tenant IDs | Vendor |
| Rollback restores pre-SSO behavior | Pilot rollback rehearsal | Timestamped runbook log + session/login checks | Joint |
No big-bang cutover
迁移是本方案唯一高不确定性部分,因此用 preflight → dry-run → pilot → expand → close 五个可逆阶段控制。
validate config + cert
classify mappings
5–10 users
tenant flag cohorts
| Gate | Proceed when | Stop / rollback when | Rollback action |
|---|---|---|---|
| Enable pilot | Connection test passes; conflict rate <5% | Certificate/config invalid | Keep flag off; no user impact |
| Expand cohort | ≥20 successful logins; zero wrong links | Any wrong link or login failure >2% | Disable tenant flag; revoke pilot links |
| Default SSO | 7-day stable pilot; support runbook ready | IdP availability below agreed SLO | Restore login choice default |
| Close rollout | 30-day stable; audit reviewed | Unresolved identity conflicts | Hold expansion; local login remains |
Decisions already made
这些不是开放式设计问题;Vendor 实现必须遵循相同行为,避免重复分析和范围膨胀。
不得按 email 静默绑定。进入 conflict queue,由管理员核验后显式 link。
在 identity lookup 前拒绝;缓存 assertion ID 至 NotOnOrAfter + clock skew。
允许 active + next certificate;新证书预验证后切换,旧证书保留 24 小时。
本期不做 role sync。授权继续由本地 Permission Engine 决定。
user inactive/deleted 时不创建 session;记录 identity.orphaned 事件。
使用 config_version 乐观锁;后保存者必须 reload 后重试。
Definition of ready / done
双方在开工前冻结边界,在验收时按证据关闭任务。
Approve the design, cap the effort.
以本文组件、契约和验收边界作为 Vendor 实施基线;建议批准 29–34 人天,并要求任何新增估算都指向具体的新范围或可验证风险。